// 版权归Go作者所有。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

// 寄存器分配。
// None
// 我们使用线性扫描寄存器分配器的一个版本。我们对待
// 整个功能作为单个长基本块并贯穿
// 它使用贪婪的寄存器分配器。然后所有边合并
// （那些以len（Preds）>1的块为目标的块被处理为
// 将数据洗牌到边缘目标期望的位置。
// None
// 贪婪的分配器将值移动到寄存器中，就在它们移动之前
// 使用时，仅在必要时进行泄漏登记，并且泄漏
// 未来下一次使用最远的值。
// None
// 寄存器分配器要求在
// 至少有一个前辈已经被安排好了。最近的
// 这样的前置器为块提供起始寄存器状态。
// None
// 它还要求没有临界边（临界边）=
// 来自具有>1个后继项的块，并转到具有>1个后继项的块
// 前任）。这使得在合并边上添加修复代码变得很容易-
// 合并边的源只有一个后继边，因此我们可以添加
// 将代码修正到该块的末尾。

// 溢出
// None
// 在分配器的正常过程中，我们可能会抛出一个仍然有效的
// 所有寄存器中的值。当随后使用该值时，我们必须
// 从堆栈上的插槽加载它。我们还必须发出指示
// 使用v的副本初始化堆栈位置。
// None
// 预regalloc：
// （1） v=操作。。。
// （2） x=运算。。。
// (3) ... = 作品v。。。
// None
// 后regalloc：
// （1） v=Op…：斧头
// s=STOREG v
// （2） x=Op…：斧头
// c=加载注册表s:CX
// (3) ... = Op c。。。
// None
// 分配一直正常进行，直到我们达到（3）并且我们意识到我们已经
// v的用法，它不在任何寄存器中。在这一点上，我们分配
// 为v。我们无法确定正确的地点
// 此时溢出，因此我们最初将溢出分配为无块。
// 然后生成还原以将v加载回寄存器中，以便
// 被使用。v的后续使用将使用恢复的值c。
// None
// 剩下的问题是在哪里安排泄漏。
// 在分配期间，我们跟踪v的所有恢复的控制者。
// v的溢出一定会控制那个街区。泄漏也必须在
// v仍在寄存器中的点。
// None
// 要找到正确的位置，请从控制所有恢复的块b开始。
// -如果b是v.阻塞，则在v.阻塞之后立即发布溢出。
// 众所周知，此时它位于寄存器中，并控制任何恢复。
// -否则，如果v位于b开头的寄存器中，
// 把v的溢出放在b的开头。
// -否则，设置b=b的直接支配者，并重复。
// None
// 和往常一样，Phi值是特殊的。我们定义了两种类型的潜在危险装置
// 合并发生在寄存器中（“寄存器”phi）和
// 合并发生在堆栈位置（“堆栈”φ）。
// None
// 寄存器phi必须将phi及其所有输入分配给
// 相同的寄存器。注册潜在危险装置的泄漏与常规操作类似。
// None
// 堆栈phi必须将phi及其所有输入分配给同一个堆栈
// 堆栈位置。Stack phi开始时生命已经溢出-每个phi
// 输入必须是相应字段末尾的存储（使用StoreReg）
// 前置块。
// b1:y=…：AX b2:z=…：BX
// y2=StoreReg y z2=StoreReg z
// 转到b3转到b3
// b3:x=phi（y2，z2）
// 堆栈分配器知道堆栈分配的PHI的StoreReg参数
// 必须分配给与使用它们的phi相同的堆栈插槽。
// x现在是溢出值，在首次使用之前必须出现还原。

// 待办事项

// 使用关联图标记两个应使用
// 相同的寄存器。此关联图将用于选择某些
// 用于分配的寄存器。这种亲和力有助于消除
// 是phi实施所必需的，并有助于生成分配
// 对于2寄存器体系结构。

// 注：regalloc生成的SSA输出不完全相同。如果我们有：
// None
// b1:x=…：斧头
// x2=StoreReg x
// ... AX被重新用于其他用途。。。
// 如果转到b3或b4
// None
// b3:x3=LoadReg x2:BX b4:x4=LoadReg x2:CX
// ... 使用x3。。。使用x4。。。
// None
// b2:。。。使用x3。。。
// None
// 如果b3是b2的主要前身，那么我们在b2和b2中使用x3
// 在b4末尾添加一个x4:CX->BX副本。
// 但x3的定义并不支配b2。我们真的应该
// 在b2（x5=φ（x3，x4）：BX）的开始处插入一个额外的φ以保持
// SSA表格。目前，我们忽略了这一问题，因为它仍然处于严格控制之下
// regalloc之后不需要SSA表格。我们就不使用了
// 不受x3定义支配的x3，以及CX->BX拷贝
// 将没有任何用处（因此不要在regalloc之后运行死代码！）。
// TODO:也许我们应该引入这些额外的网络钓鱼？

package ssa

import (
	"cmd/compile/internal/base"
	"cmd/compile/internal/ir"
	"cmd/compile/internal/types"
	"cmd/internal/src"
	"cmd/internal/sys"
	"fmt"
	"internal/buildcfg"
	"math/bits"
	"unsafe"
)

const (
	moveSpills = iota
	logSpills
	regDebug
	stackDebug
)

// 距离是一种度量值，用于度量未来值的使用距离。
// 距离是以指令为单位测量的。
const (
	likelyDistance   = 1
	normalDistance   = 10
	unlikelyDistance = 100
)

// regalloc对f执行寄存器分配。它设置了f.RegAlloc
// 分配给最终的分配。
func regalloc(f *Func) {
	var s regAllocState
	s.init(f)
	s.regalloc(f)
}

type register uint8

const noRegister register = 255

// 用于批量初始化
var noRegisters [32]register = [32]register{
	noRegister, noRegister, noRegister, noRegister, noRegister, noRegister, noRegister, noRegister,
	noRegister, noRegister, noRegister, noRegister, noRegister, noRegister, noRegister, noRegister,
	noRegister, noRegister, noRegister, noRegister, noRegister, noRegister, noRegister, noRegister,
	noRegister, noRegister, noRegister, noRegister, noRegister, noRegister, noRegister, noRegister,
}

// regMask对一组机器寄存器进行编码。
// TODO:regMask->regSet？
type regMask uint64

func (m regMask) String() string {
	s := ""
	for r := register(0); m != 0; r++ {
		if m>>r&1 == 0 {
			continue
		}
		m &^= regMask(1) << r
		if s != "" {
			s += " "
		}
		s += fmt.Sprintf("r%d", r)
	}
	return s
}

func (s *regAllocState) RegMaskString(m regMask) string {
	str := ""
	for r := register(0); m != 0; r++ {
		if m>>r&1 == 0 {
			continue
		}
		m &^= regMask(1) << r
		if str != "" {
			str += " "
		}
		str += s.registers[r].String()
	}
	return str
}

// countRegs返回寄存器掩码中的设置位数。
func countRegs(r regMask) int {
	return bits.OnesCount64(uint64(r))
}

// pickReg从寄存器掩码中拾取任意寄存器。
func pickReg(r regMask) register {
	if r == 0 {
		panic("can't pick a register from an empty set")
	}
	// 挑最低的
	return register(bits.TrailingZeros64(uint64(r)))
}

type use struct {
	dist int32    // 从块的起点到使用值的距离
	pos  src.XPos // 使用的来源位置
	next *use     // 按非减损dist顺序使用值的链接列表
}

// valState记录（预regalloc）值的寄存器分配状态。
type valState struct {
	regs              regMask // 保存一个值的寄存器集（通常只有一个）
	uses              *use    // 此块中的使用列表
	spill             *Value  // 溢出的值副本（如有）
	restoreMin        int32   // 所有恢复“块”sdom条目的最小值
	restoreMax        int32   // 所有恢复“块”sdom.exit的最大值
	needReg           bool    // 缓存的价值！v、 键入.IsMemory（）&&！v、 键入.IsVoid（）&&！。v、 Type.IsFlags（）
	rematerializeable bool    // v.ReMaterializable（）的缓存值
}

type regState struct {
	v *Value // 存储在此寄存器中的原始（preregalloc）值。
	c *Value // 当前在寄存器中等于v的值。可能是v或它的副本。
	// 如果寄存器未使用，则v==c==nil
}

type regAllocState struct {
	f *Func

	sdom        SparseTree
	registers   []Register
	numRegs     register
	SPReg       register
	SBReg       register
	GReg        register
	allocatable regMask

	// 每个块末尾的活动值。live[b.ID]是值ID的列表
	// 哪些是活在b的末尾，加上一个计数有多少条指令
	// 转发到下一次使用。
	live [][]liveInfo
	// 在每个块的末尾指定所需的寄存器。
	// 请注意，这是在分配之前计算的静态映射。动态
	// 登记愿望（部分完成的分配）将胜过
	// 这个信息。
	desired []desiredState

	// 每个（preregalloc）值的当前状态
	values []valState

	// SP、SB值的ID
	sp, sb ID

	// 对于每个值，将其值ID映射回
	// 它是从中导出的preregalloc值。
	orig []*Value

	// 每个寄存器的当前状态
	regs []regState

	// 包含不能被踢出的值的寄存器
	nospill regMask

	// 当前正在使用的寄存器的掩码
	used regMask

	// 当前指令中使用的寄存器掩码
	tmpused regMask

	// 我们正在处理的当前区块
	curBlock *Block

	// 使用记录的缓存
	freeUseRecords *use

	// endRegs[blockid]是每个块末尾的寄存器状态。
	// 编码为一组endReg记录。
	endRegs [][]endReg

	// startRegs[blockid]是合并块开始时的寄存器状态。
	// 保存状态不包括块中phi ops的状态。
	startRegs [][]startReg

	// spillLive[blockid]是每个块末尾的实时溢出集
	spillLive [][]ID

	// 我们生成的一组拷贝，用于移动物体
	// 是否用于洗牌。未使用的副本将被删除。
	copies map[*Value]bool

	loopnest *loopnest

	// 选择访问区块以进行分配的良好顺序。
	visitOrder []*Block

	// blockOrder[b.ID]对应于visitOrder中块b的索引。
	blockOrder []int32

	// 是否插入在调用站点上关闭死机寄存器的指令
	doClobber bool
}

type endReg struct {
	r register
	v *Value // 此寄存器中保留的pre-regalloc值（TODO:我们可以在此处使用ID吗？）
	c *Value // 值的缓存版本
}

type startReg struct {
	r   register
	v   *Value   // 此寄存器中需要的pre-regalloc值
	c   *Value   // 值的缓存版本
	pos src.XPos // 使用此寄存器的源位置
}

// freeReg释放寄存器r。r的任何当前用户都将被踢出。
func (s *regAllocState) freeReg(r register) {
	v := s.regs[r].v
	if v == nil {
		s.f.Fatalf("tried to free an already free register %d\n", r)
	}

	// 将r标记为未使用。
	if s.f.pass.debug > regDebug {
		fmt.Printf("freeReg %s (dump %s/%s)\n", &s.registers[r], v, s.regs[r].c)
	}
	s.regs[r] = regState{}
	s.values[v.ID].regs &^= regMask(1) << r
	s.used &^= regMask(1) << r
}

// freeRegs释放m中列出的所有寄存器。
func (s *regAllocState) freeRegs(m regMask) {
	for m&s.used != 0 {
		s.freeReg(pickReg(m & s.used))
	}
}

// clobberRegs插入clobber寄存器在m中列出的指令。
func (s *regAllocState) clobberRegs(m regMask) {
	m &= s.allocatable & s.f.Config.gpRegMask // 只有整数寄存器才能包含指针，只有对它们进行删除
	for m != 0 {
		r := pickReg(m)
		m &^= 1 << r
		x := s.curBlock.NewValue0(src.NoXPos, OpClobberReg, types.TypeVoid)
		s.f.setHome(x, &s.registers[r])
	}
}

// setOrig记录c的原始值与
// v的原始值。
func (s *regAllocState) setOrig(c *Value, v *Value) {
	for int(c.ID) >= len(s.orig) {
		s.orig = append(s.orig, nil)
	}
	if s.orig[c.ID] != nil {
		s.f.Fatalf("orig value set twice %s %s", c, v)
	}
	s.orig[c.ID] = s.orig[v.ID]
}

// assignReg将寄存器r分配给c，即v的副本。
// r必须是未使用的。
func (s *regAllocState) assignReg(r register, v *Value, c *Value) {
	if s.f.pass.debug > regDebug {
		fmt.Printf("assignReg %s %s/%s\n", &s.registers[r], v, c)
	}
	if s.regs[r].v != nil {
		s.f.Fatalf("tried to assign register %d to %s/%s but it is already used by %s", r, v, c, s.regs[r].v)
	}

	// 更新状态。
	s.regs[r] = regState{v, c}
	s.values[v.ID].regs |= regMask(1) << r
	s.used |= regMask(1) << r
	s.f.setHome(c, &s.registers[r])
}

// allocReg从掩码中的寄存器集中选择一个寄存器。
// 如果没有未使用的寄存器，则会从中踢出一个值
// 用来腾出地方的登记簿。
func (s *regAllocState) allocReg(mask regMask, v *Value) register {
	if v.OnWasmStack {
		return noRegister
	}

	mask &= s.allocatable
	mask &^= s.nospill
	if mask == 0 {
		s.f.Fatalf("no register available for %s", v.LongString())
	}

	// 选择一个未使用的寄存器（如果有）。
	if mask&^s.used != 0 {
		return pickReg(mask &^ s.used)
	}

	// 选择要溢出的值。将值与
	// 在未来使用中最远的。
	// TODO:首选已溢出值的寄存器？
	// TODO:使用关联图修改首选项。
	// TODO:如果一个值在多个寄存器中，请溢出其中一个寄存器
	// 在将值溢出到单个寄存器之前。

	// 找一个寄存器溢出。我们溢出了包含该值的寄存器
	// 它的下一个用途是在尽可能远的将来。
	// https:
	var r register
	maxuse := int32(-1)
	for t := register(0); t < s.numRegs; t++ {
		if mask>>t&1 == 0 {
			continue
		}
		v := s.regs[t].v
		if n := s.values[v.ID].uses.dist; n > maxuse {
			// v的下一次使用比任何价值都要远
			// 到目前为止我们已经看到了。新的最佳人选。
			r = t
			maxuse = n
		}
	}
	if maxuse == -1 {
		s.f.Fatalf("couldn't find register to spill")
	}

	if s.f.Config.ctxt.Arch.Arch == sys.ArchWasm {
		// 托多（尼兰斯）：理论上这永远不会发生，因为所有wasm寄存器都是相等的。
		// 因此，如果仍然有一个空闲寄存器，那么分配首先应该选择该寄存器，而不是
		// 试图把其他价值踢出去。在实践中，这种情况确实发生了，它破坏了堆栈优化。
		s.freeReg(r)
		return r
	}

	// 如果有免费注册的话，试着在退出之前移动它。
	// 我们生成一个副本并记录它。如果从未使用过，它将被删除。
	v2 := s.regs[r].v
	m := s.compatRegs(v2.Type) &^ s.used &^ s.tmpused &^ (regMask(1) << r)
	if m != 0 && !s.values[v2.ID].rematerializeable && countRegs(s.values[v2.ID].regs) == 1 {
		r2 := pickReg(m)
		c := s.curBlock.NewValue1(v2.Pos, OpCopy, v2.Type, s.regs[r].c)
		s.copies[c] = false
		if s.f.pass.debug > regDebug {
			fmt.Printf("copy %s to %s : %s\n", v2, c, &s.registers[r2])
		}
		s.setOrig(c, v2)
		s.assignReg(r2, v2, c)
	}
	s.freeReg(r)
	return r
}

// makeSpill返回一个值，该值表示v的溢出值。
// b是使用溢出物的区块。
func (s *regAllocState) makeSpill(v *Value, b *Block) *Value {
	vi := &s.values[v.ID]
	if vi.spill != nil {
		// 最后一个块未知-跟踪还原所在的子树。
		vi.restoreMin = min32(vi.restoreMin, s.sdom[b.ID].entry)
		vi.restoreMax = max32(vi.restoreMax, s.sdom[b.ID].exit)
		return vi.spill
	}
	// 给v.泼洒。我们不知道我们要去哪里
	// 把它放在这里，所以我们暂时不封锁它。
	spill := s.f.newValueNoBlock(OpStoreReg, v.Type, v.Pos)
	// 我们也不知道泄漏的arg是什么。
	// 暂时不要争论。
	s.setOrig(spill, v)
	vi.spill = spill
	vi.restoreMin = s.sdom[b.ID].entry
	vi.restoreMax = s.sdom[b.ID].exit
	return spill
}

// allocValToReg将v分配给从regMask和中选择的寄存器
// 返回v的寄存器副本。任何以前的用户都会被踢出并溢出
// （如有必要）。加载代码在当前电脑上添加。如果设置了nospill
// 已分配寄存器标记为nospill，因此无法执行分配
// 撤消此操作，直到调用者通过清除nospill允许为止。返回一个
// *值，该值是分配给所选寄存器的v或v的副本。
func (s *regAllocState) allocValToReg(v *Value, mask regMask, nospill bool, pos src.XPos) *Value {
	if s.f.Config.ctxt.Arch.Arch == sys.ArchWasm && v.rematerializeable() {
		c := v.copyIntoWithXPos(s.curBlock, pos)
		c.OnWasmStack = true
		s.setOrig(c, v)
		return c
	}
	if v.OnWasmStack {
		return v
	}

	vi := &s.values[v.ID]
	pos = pos.WithNotStmt()
	// 检查v是否已在请求的寄存器中。
	if mask&vi.regs != 0 {
		r := pickReg(mask & vi.regs)
		if s.regs[r].v != v || s.regs[r].c == nil {
			panic("bad register state")
		}
		if nospill {
			s.nospill |= regMask(1) << r
		}
		return s.regs[r].c
	}

	var r register
	// 如果设置了nospill，则会立即使用该值，这样它就可以存在于WebAssembly堆栈中。
	onWasmStack := nospill && s.f.Config.ctxt.Arch.Arch == sys.ArchWasm
	if !onWasmStack {
		// 分配一个寄存器。
		r = s.allocReg(mask, v)
	}

	// 将v分配到新寄存器。
	var c *Value
	if vi.regs != 0 {
		// 从v已在的寄存器中复制。
		r2 := pickReg(vi.regs)
		if s.regs[r2].v != v {
			panic("bad register state")
		}
		c = s.curBlock.NewValue1(pos, OpCopy, v.Type, s.regs[r2].c)
	} else if v.rematerializeable() {
		// 重新材料化，而不是从溢出位置装载。
		c = v.copyIntoWithXPos(s.curBlock, pos)
	} else {
		// 从溢出位置加载v。
		spill := s.makeSpill(v, s.curBlock)
		if s.f.pass.debug > logSpills {
			s.f.Warnl(vi.spill.Pos, "load spill for %v from %v", v, spill)
		}
		c = s.curBlock.NewValue1(pos, OpLoadReg, v.Type, spill)
	}

	s.setOrig(c, v)

	if onWasmStack {
		c.OnWasmStack = true
		return c
	}

	s.assignReg(r, v, c)
	if c.Op == OpLoadReg && s.isGReg(r) {
		s.f.Fatalf("allocValToReg.OpLoadReg targeting g: " + c.LongString())
	}
	if nospill {
		s.nospill |= regMask(1) << r
	}
	return c
}

// isLeaf报告isLeaf是否执行任何调用。
func isLeaf(f *Func) bool {
	for _, b := range f.Blocks {
		for _, v := range b.Values {
			if opcodeTable[v.Op].call {
				return false
			}
		}
	}
	return true
}

func (s *regAllocState) init(f *Func) {
	s.f = f
	s.f.RegAlloc = s.f.Cache.locs[:0]
	s.registers = f.Config.registers
	if nr := len(s.registers); nr == 0 || nr > int(noRegister) || nr > int(unsafe.Sizeof(regMask(0))*8) {
		s.f.Fatalf("bad number of registers: %d", nr)
	} else {
		s.numRegs = register(nr)
	}
	// 找到SP、SB和g寄存器。
	s.SPReg = noRegister
	s.SBReg = noRegister
	s.GReg = noRegister
	for r := register(0); r < s.numRegs; r++ {
		switch s.registers[r].String() {
		case "SP":
			s.SPReg = r
		case "SB":
			s.SBReg = r
		case "g":
			s.GReg = r
		}
	}
	// 确保我们找到了所有必需的寄存器。
	switch noRegister {
	case s.SPReg:
		s.f.Fatalf("no SP register found")
	case s.SBReg:
		s.f.Fatalf("no SB register found")
	case s.GReg:
		if f.Config.hasGReg {
			s.f.Fatalf("no g register found")
		}
	}

	// 弄清楚我们可以使用哪些寄存器。
	s.allocatable = s.f.Config.gpRegMask | s.f.Config.fpRegMask | s.f.Config.specialRegMask
	s.allocatable &^= 1 << s.SPReg
	s.allocatable &^= 1 << s.SBReg
	if s.f.Config.hasGReg {
		s.allocatable &^= 1 << s.GReg
	}
	if buildcfg.FramePointerEnabled && s.f.Config.FPReg >= 0 {
		s.allocatable &^= 1 << uint(s.f.Config.FPReg)
	}
	if s.f.Config.LinkReg != -1 {
		if isLeaf(f) {
			// 叶函数不保存/恢复链接寄存器。
			s.allocatable &^= 1 << uint(s.f.Config.LinkReg)
		}
	}
	if s.f.Config.ctxt.Flag_dynlink {
		switch s.f.Config.arch {
		case "amd64":
			s.allocatable &^= 1 << 15 // R15
		case "arm":
			s.allocatable &^= 1 << 9 // R9
		case "ppc64le": // R2已经预订了。
			// 无事可做
		case "arm64":
			// 无事可做？
		case "386":
			// 无事可做。
			// 注意，对于标志_共享（位置独立代码）
			// 我们确实需要小心，但这种谨慎是隐藏的
			// 在重写规则中，所以我们总是有一个空闲寄存器
			// 可用于全局加载/存储。参见gen/386.1规则（搜索共享标志）。
		case "s390x":
			s.allocatable &^= 1 << 11 // R11
		default:
			s.f.fe.Fatalf(src.NoXPos, "arch %s not implemented", s.f.Config.arch)
		}
	}

	// 线性扫描寄存器分配可能受块出现顺序的影响。
	// 将寄存器分配顺序与生成的块顺序解耦。
	// 这也为实验找到更好的顺序创造了机会。
	s.visitOrder = layoutRegallocOrder(f)

	// 计算块顺序。该阵列允许我们区分前边缘
	// 从后向边开始，计算它们走多远。
	s.blockOrder = make([]int32, f.NumBlocks())
	for i, b := range s.visitOrder {
		s.blockOrder[b.ID] = int32(i)
	}

	s.regs = make([]regState, s.numRegs)
	nv := f.NumValues()
	if cap(s.f.Cache.regallocValues) >= nv {
		s.f.Cache.regallocValues = s.f.Cache.regallocValues[:nv]
	} else {
		s.f.Cache.regallocValues = make([]valState, nv)
	}
	s.values = s.f.Cache.regallocValues
	s.orig = make([]*Value, nv)
	s.copies = make(map[*Value]bool)
	for _, b := range s.visitOrder {
		for _, v := range b.Values {
			if !v.Type.IsMemory() && !v.Type.IsVoid() && !v.Type.IsFlags() && !v.Type.IsTuple() {
				s.values[v.ID].needReg = true
				s.values[v.ID].rematerializeable = v.rematerializeable()
				s.orig[v.ID] = v
			}
			// 注意：对于返回元组类型的值，needReg为false。
			// 相反，我们将相应的选择标记为needReg。
		}
	}
	s.computeLive()

	s.endRegs = make([][]endReg, f.NumBlocks())
	s.startRegs = make([][]startReg, f.NumBlocks())
	s.spillLive = make([][]ID, f.NumBlocks())
	s.sdom = f.Sdom()

	// wasm：标记可以优化的指令，使其值仅在WebAssembly堆栈上。
	if f.Config.ctxt.Arch.Arch == sys.ArchWasm {
		canLiveOnStack := f.newSparseSet(f.NumValues())
		defer f.retSparseSet(canLiveOnStack)
		for _, b := range f.Blocks {
			// 新街区。清除候选集。
			canLiveOnStack.clear()
			for _, c := range b.ControlValues() {
				if c.Uses == 1 && !opcodeTable[c.Op].generic {
					canLiveOnStack.add(c.ID)
				}
			}
			// 向后走。
			for i := len(b.Values) - 1; i >= 0; i-- {
				v := b.Values[i]
				if canLiveOnStack.contains(v.ID) {
					v.OnWasmStack = true
				} else {
					// 值不能存在于堆栈上。不允许对值进行重新排序，因此请清除候选集。
					canLiveOnStack.clear()
				}
				for _, arg := range v.Args {
					// 如果满足以下条件，则值可以存在于堆栈上：
					// -它只使用一次
					// -它在同一基本块中使用
					// -它不是一个“mem”值
					// -这是一个WebAssembly操作
					if arg.Uses == 1 && arg.Block == v.Block && !arg.Type.IsMemory() && !opcodeTable[arg.Op].generic {
						canLiveOnStack.add(arg.ID)
					}
				}
			}
		}
	}

	// ClobberDedReg实验将代码插入到clobber死寄存器中
	// 在呼叫站点。
	// 忽略巨大的功能以避免做太多的工作。
	if base.Flag.ClobberDeadReg && len(s.f.Blocks) <= 10000 {
		// 托多：尊敬戈克洛伯·达哈什，或者也许是戈萨哈什。
		s.doClobber = true
	}
}

// 在距块起点的距离处添加id的使用记录。
// 对addUse的所有调用都必须以非递增的距离进行。
func (s *regAllocState) addUse(id ID, dist int32, pos src.XPos) {
	r := s.freeUseRecords
	if r != nil {
		s.freeUseRecords = r.next
	} else {
		r = &use{}
	}
	r.dist = dist
	r.pos = pos
	r.next = s.values[id].uses
	s.values[id].uses = r
	if r.next != nil && dist > r.next.dist {
		s.f.Fatalf("uses added in wrong order")
	}
}

// advanceUses将v的参数的使用从v之前的状态提前到v之后的状态。
// 不再使用的任何值都将从寄存器中释放。
func (s *regAllocState) advanceUses(v *Value) {
	for _, a := range v.Args {
		if !s.values[a.ID].needReg {
			continue
		}
		ai := &s.values[a.ID]
		r := ai.uses
		ai.uses = r.next
		if r.next == nil {
			// 值为空，释放所有保存该值的寄存器。
			s.freeRegs(ai.regs)
		}
		r.next = s.freeUseRecords
		s.freeUseRecords = r
	}
}

// liveAfterCurrentInstruction报告v在之后是否处于活动状态
// 当前指令已完成。v必须由
// 当前指令。
func (s *regAllocState) liveAfterCurrentInstruction(v *Value) bool {
	u := s.values[v.ID].uses
	if u == nil {
		panic(fmt.Errorf("u is nil, v = %s, s.values[v.ID] = %v", v.LongString(), s.values[v.ID]))
	}
	d := u.dist
	for u != nil && u.dist == d {
		u = u.next
	}
	return u != nil && u.dist > d
}

// 将寄存器的状态设置为在regs中编码的状态。
func (s *regAllocState) setState(regs []endReg) {
	s.freeRegs(s.used)
	for _, x := range regs {
		s.assignReg(x.r, x.v, x.c)
	}
}

// compatRegs返回可存储类型t的寄存器集。
func (s *regAllocState) compatRegs(t *types.Type) regMask {
	var m regMask
	if t.IsTuple() || t.IsFlags() {
		return 0
	}
	if t.IsFloat() || t == types.TypeInt128 {
		if t.Kind() == types.TFLOAT32 && s.f.Config.fp32RegMask != 0 {
			m = s.f.Config.fp32RegMask
		} else if t.Kind() == types.TFLOAT64 && s.f.Config.fp64RegMask != 0 {
			m = s.f.Config.fp64RegMask
		} else {
			m = s.f.Config.fpRegMask
		}
	} else {
		m = s.f.Config.gpRegMask
	}
	return m & s.allocatable
}

// regspec返回操作op的regInfo。
func (s *regAllocState) regspec(v *Value) regInfo {
	op := v.Op
	if op == OpConvert {
		// OpConvert是一个泛型op，因此它没有
		// 在静态表中设置的寄存器。它可以使用任何
		// 可分配整数寄存器。
		m := s.allocatable & s.f.Config.gpRegMask
		return regInfo{inputs: []inputInfo{{regs: m}}, outputs: []outputInfo{{regs: m}}}
	}
	if op == OpArgIntReg {
		reg := v.Block.Func.Config.intParamRegs[v.AuxInt8()]
		return regInfo{outputs: []outputInfo{{regs: 1 << uint(reg)}}}
	}
	if op == OpArgFloatReg {
		reg := v.Block.Func.Config.floatParamRegs[v.AuxInt8()]
		return regInfo{outputs: []outputInfo{{regs: 1 << uint(reg)}}}
	}
	if op.IsCall() {
		if ac, ok := v.Aux.(*AuxCall); ok && ac.reg != nil {
			return *ac.Reg(&opcodeTable[op].reg, s.f.Config)
		}
	}
	if op == OpMakeResult && s.f.OwnAux.reg != nil {
		return *s.f.OwnAux.ResultReg(s.f.Config)
	}
	return opcodeTable[op].reg
}

func (s *regAllocState) isGReg(r register) bool {
	return s.f.Config.hasGReg && s.GReg == r
}

func (s *regAllocState) regalloc(f *Func) {
	regValLiveSet := f.newSparseSet(f.NumValues()) // 寄存器中可能存在的一组值
	defer f.retSparseSet(regValLiveSet)
	var oldSched []*Value
	var phis []*Value
	var phiRegs []register
	var args []*Value

	// 用于计算所需寄存器的数据结构。
	var desired desiredState

	// 块中每条指令的输入和输出所需的寄存器。
	type dentry struct {
		out [4]register    // 期望输出寄存器
		in  [3][4]register // 所需输入寄存器（用于输入0、1和2）
	}
	var dinfo []dentry

	if f.Entry != f.Blocks[0] {
		f.Fatalf("entry block must be first")
	}

	for _, b := range s.visitOrder {
		if s.f.pass.debug > regDebug {
			fmt.Printf("Begin processing block %v\n", b)
		}
		s.curBlock = b

		// 初始化regValLiveSet并使用此块的字段。
		// 在执行活动性分析时，在块中向后走。
		regValLiveSet.clear()
		for _, e := range s.live[b.ID] {
			s.addUse(e.ID, int32(len(b.Values))+e.dist, e.pos) // 从块的末尾开始的伪使用
			regValLiveSet.add(e.ID)
		}
		for _, v := range b.ControlValues() {
			if s.values[v.ID].needReg {
				s.addUse(v.ID, int32(len(b.Values)), b.Pos) // 控制值的伪使用
				regValLiveSet.add(v.ID)
			}
		}
		for i := len(b.Values) - 1; i >= 0; i-- {
			v := b.Values[i]
			regValLiveSet.remove(v.ID)
			if v.Op == OpPhi {
				// 从活动集中删除v，但不要添加
				// 任何输入。这是len（b.Preds）>1的状态
				// 低于欲望的情况；它想专门处理网络钓鱼。
				continue
			}
			if opcodeTable[v.Op].call {
				// 函数调用会清除除SP和SB之外的所有寄存器。
				regValLiveSet.clear()
				if s.sp != 0 && s.values[s.sp].uses != nil {
					regValLiveSet.add(s.sp)
				}
				if s.sb != 0 && s.values[s.sb].uses != nil {
					regValLiveSet.add(s.sb)
				}
			}
			for _, a := range v.Args {
				if !s.values[a.ID].needReg {
					continue
				}
				s.addUse(a.ID, int32(i), v.Pos)
				regValLiveSet.add(a.ID)
			}
		}
		if s.f.pass.debug > regDebug {
			fmt.Printf("use distances for %s\n", b)
			for i := range s.values {
				vi := &s.values[i]
				u := vi.uses
				if u == nil {
					continue
				}
				fmt.Printf("  v%d:", i)
				for u != nil {
					fmt.Printf(" %d", u.dist)
					u = u.next
				}
				fmt.Println()
			}
		}

		// 制作块明细表的副本，以便我们可以在适当的位置生成新的块明细表。
		// 我们为PHI和常规值制作单独的副本。
		nphi := 0
		for _, v := range b.Values {
			if v.Op != OpPhi {
				break
			}
			nphi++
		}
		phis = append(phis[:0], b.Values[:nphi]...)
		oldSched = append(oldSched[:0], b.Values[nphi:]...)
		b.Values = b.Values[:0]

		// 初始化块的开始状态。
		if b == f.Entry {
			// 要启动，Regalloc状态为空。
			if nphi > 0 {
				f.Fatalf("phis in entry block")
			}
		} else if len(b.Preds) == 1 {
			// 使用上一个块的结束状态启动regalloc状态。
			s.setState(s.endRegs[b.Preds[0].b.ID])
			if nphi > 0 {
				f.Fatalf("phis in single-predecessor block")
			}
			// 删除任何不再有效的值。
			// 这可能是因为在p的末尾，一个值可能是
			// 活着但只被p的其他继承人使用。
			for r := register(0); r < s.numRegs; r++ {
				v := s.regs[r].v
				if v != nil && !regValLiveSet.contains(v.ID) {
					s.freeReg(r)
				}
			}
		} else {
			// 这是一个复杂的案例。我们有不止一个前任，
			// 这意味着我们可能会有Phi行动。

			// 从溢出值最少的前置寄存器的最终寄存器状态开始。
			// 这是基于以下几点：
			// 1，溢出值越小，表示该路径的寄存器压力越小，
			// 因此，该块的值更有可能分配给寄存器。
			// 2、避免包含函数调用的前置程序，因为前置程序
			// 包含函数调用通常会产生大量溢出并丢失以前的
			// 分配状态。
			// TODO:改进此部分。至少前一个版本的endRegs的大小也是如此
			// 对代码大小和编译器速度的影响。但要找到一个简单的解决方案并不容易
			// 综合多种因素的有效方法。
			idx := -1
			for i, p := range b.Preds {
				// 如果尚未访问前置程序，请跳过它，因为它处于结束状态
				// （redRegs和spillLive）尚未计算。
				pb := p.b
				if s.blockOrder[pb.ID] >= s.blockOrder[b.ID] {
					continue
				}
				if idx == -1 {
					idx = i
					continue
				}
				pSel := b.Preds[idx].b
				if len(s.spillLive[pb.ID]) < len(s.spillLive[pSel.ID]) {
					idx = i
				} else if len(s.spillLive[pb.ID]) == len(s.spillLive[pSel.ID]) {
					// 使用一些可能的信息。临界通过后，pb和pSel必须
					// 是普通块，因此选中边pb->pb.Preds而不是边pb->b。
					// TODO:改进对可能的前辈的预测。以下
					// 该方法仅适用于最简单的情况。对于复杂的情况，
					// 预测可能不准确，但这不会影响预测结果
					// 程序的正确性。
					// 根据布局算法，前一个具有
					// 较小的blockOrder是真正的分支，测试结果显示
					// 最好选择具有较小尺寸的前置机
					// 区块订单比没有选择要好。
					if pb.likelyBranch() && !pSel.likelyBranch() || s.blockOrder[pb.ID] < s.blockOrder[pSel.ID] {
						idx = i
					}
				}
			}
			if idx < 0 {
				f.Fatalf("bad visitOrder, no predecessor of %s has been visited before it", b)
			}
			p := b.Preds[idx].b
			s.setState(s.endRegs[p.ID])

			if s.f.pass.debug > regDebug {
				fmt.Printf("starting merge block %s with end state of %s:\n", b, p)
				for _, x := range s.endRegs[p.ID] {
					fmt.Printf("  %s: orig:%s cache:%s\n", &s.registers[x.r], x.v, x.c)
				}
			}

			// 决定phi ops的寄存器。使用已确定的寄存器
			// 如果可以的话，由主要的前任。
			// TODO:选择最好的（已处理的）前辈？
			// 多数票？最深嵌套层？
			phiRegs = phiRegs[:0]
			var phiUsed regMask

			for _, v := range phis {
				if !s.values[v.ID].needReg {
					phiRegs = append(phiRegs, noRegister)
					continue
				}
				a := v.Args[idx]
				// 某些指令以不可分配寄存器为目标。
				// 它们不适合进一步（φ函数）分配。
				m := s.values[a.ID].regs &^ phiUsed & s.allocatable
				if m != 0 {
					r := pickReg(m)
					phiUsed |= regMask(1) << r
					phiRegs = append(phiRegs, r)
				} else {
					phiRegs = append(phiRegs, noRegister)
				}
			}

			// 第二次通过-取消分配寄存器中的所有phi输入。
			for i, v := range phis {
				if !s.values[v.ID].needReg {
					continue
				}
				a := v.Args[idx]
				r := phiRegs[i]
				if r == noRegister {
					continue
				}
				if regValLiveSet.contains(a.ID) {
					// 输入值仍然有效（由Phi以外的其他对象使用）。
					// 如果有免费注册的话，试着在退出之前移动它。
					// 我们在前置块中生成一个副本并记录它。会的
					// 如果从未使用，请稍后删除。
					// None
					// 选择一个免费的注册表。此时，前置程序中使用了一些寄存器
					// 区块可能已解除分配。这些是用于网络钓鱼的。排除
					// 他们（而且他们无论如何也不会有帮助）。
					m := s.compatRegs(a.Type) &^ s.used &^ phiUsed
					if m != 0 && !s.values[a.ID].rematerializeable && countRegs(s.values[a.ID].regs) == 1 {
						r2 := pickReg(m)
						c := p.NewValue1(a.Pos, OpCopy, a.Type, s.regs[r].c)
						s.copies[c] = false
						if s.f.pass.debug > regDebug {
							fmt.Printf("copy %s to %s : %s\n", a, c, &s.registers[r2])
						}
						s.setOrig(c, a)
						s.assignReg(r2, a, c)
						s.endRegs[p.ID] = append(s.endRegs[p.ID], endReg{r2, a, c})
					}
				}
				s.freeReg(r)
			}

			// 将phi ops复制到新计划中。
			b.Values = append(b.Values, phis...)

			// 第三次通过-为输入
			// 不在主前置程序的寄存器中。
			for i, v := range phis {
				if !s.values[v.ID].needReg {
					continue
				}
				if phiRegs[i] != noRegister {
					continue
				}
				m := s.compatRegs(v.Type) &^ phiUsed &^ s.used
				// 如果v的其他输入之一在寄存器中，且寄存器可用，
				// 选择此寄存器，它可以保存一些不必要的副本。
				for i, pe := range b.Preds {
					if i == idx {
						continue
					}
					ri := noRegister
					for _, er := range s.endRegs[pe.b.ID] {
						if er.v == s.orig[v.Args[i].ID] {
							ri = er.r
							break
						}
					}
					if ri != noRegister && m>>ri&1 != 0 {
						m = regMask(1) << ri
						break
					}
				}
				if m != 0 {
					r := pickReg(m)
					phiRegs[i] = r
					phiUsed |= regMask(1) << r
				}
			}

			// 为网络钓鱼设置寄存器。添加phi溢出代码。
			for i, v := range phis {
				if !s.values[v.ID].needReg {
					continue
				}
				r := phiRegs[i]
				if r == noRegister {
					// 基于堆栈的phi
					// 将在下面的所有前置程序中插入溢出。
					s.values[v.ID].spill = v // v开始生命溢出
					continue
				}
				// 基于寄存器的phi
				s.assignReg(r, v, v)
			}

			// 取消分配任何不再有效的值。不包括潜在危险装置。
			for r := register(0); r < s.numRegs; r++ {
				if phiUsed>>r&1 != 0 {
					continue
				}
				v := s.regs[r].v
				if v != nil && !regValLiveSet.contains(v.ID) {
					s.freeReg(r)
				}
			}

			// 保存开始状态以供合并边使用。
			// 我们附加到一个堆栈分配的变量，我们将
			// 后来一下子复制到星际迷航中，以拯救他们
			// 关于分配。
			regList := make([]startReg, 0, 32)
			for r := register(0); r < s.numRegs; r++ {
				v := s.regs[r].v
				if v == nil {
					continue
				}
				if phiUsed>>r&1 != 0 {
					// 跳过phis使用的寄存器，我们会处理这些
					// 特别是在合并边处理过程中。
					continue
				}
				regList = append(regList, startReg{r, v, s.regs[r].c, s.values[v.ID].uses.pos})
			}
			s.startRegs[b.ID] = make([]startReg, len(regList))
			copy(s.startRegs[b.ID], regList)

			if s.f.pass.debug > regDebug {
				fmt.Printf("after phis\n")
				for _, x := range s.startRegs[b.ID] {
					fmt.Printf("  %s: v%d\n", &s.registers[x.r], x.v.ID)
				}
			}
		}

		// 分配空间以记录每个值所需的寄存器。
		if l := len(oldSched); cap(dinfo) < l {
			dinfo = make([]dentry, l)
		} else {
			dinfo = dinfo[:l]
			for i := range dinfo {
				dinfo[i] = dentry{}
			}
		}

		// 在块的末尾加载所需的静态寄存器信息。
		desired.copy(&s.desired[b.ID])

		// 在下一块开始时检查实际分配的寄存器。
		// 动态分配的寄存器将胜过静态寄存器
		// 在活性分析期间计算的所需寄存器。
		// 请注意，我们在上面设置startRegs之后执行此阶段，以便
		// 对于分支到自身的块，我们得到了正确的行为。
		for _, e := range b.Succs {
			succ := e.b
			// 待办事项：优先考虑可能的继任者？
			for _, x := range s.startRegs[succ.ID] {
				desired.add(x.v.ID, x.r)
			}
			// 成功处理phi ops。
			pidx := e.i
			for _, v := range succ.Values {
				if v.Op != OpPhi {
					break
				}
				if !s.values[v.ID].needReg {
					continue
				}
				rp, ok := s.f.getHome(v.ID).(*Register)
				if !ok {
					// 如果v未分配寄存器，则选择分配给v输入之一的寄存器。
					// 希望v稍后会被分配到该寄存器。
					// 如果输入已分配寄存器信息，则将其添加到所需，
					// 当寄存器可用时，可减少溢出或复制操作。
					for _, a := range v.Args {
						rp, ok = s.f.getHome(a.ID).(*Register)
						if ok {
							break
						}
					}
					if !ok {
						continue
					}
				}
				desired.add(v.Args[pidx].ID, register(rp.num))
			}
		}
		// 向后遍历值以计算所需的寄存器信息。
		// 更多评论请参见computeLive。
		for i := len(oldSched) - 1; i >= 0; i-- {
			v := oldSched[i]
			prefs := desired.remove(v.ID)
			regspec := s.regspec(v)
			desired.clobber(regspec.clobbers)
			for _, j := range regspec.inputs {
				if countRegs(j.regs) != 1 {
					continue
				}
				desired.clobber(j.regs)
				desired.add(v.Args[j.idx].ID, pickReg(j.regs))
			}
			if opcodeTable[v.Op].resultInArg0 {
				if opcodeTable[v.Op].commutative {
					desired.addList(v.Args[1].ID, prefs)
				}
				desired.addList(v.Args[0].ID, prefs)
			}
			// 保存此值所需的寄存器。
			dinfo[i].out = prefs
			for j, a := range v.Args {
				if j >= len(dinfo[i].in) {
					break
				}
				dinfo[i].in[j] = desired.get(a.ID)
			}
		}

		// 处理所有非φ值。
		for idx, v := range oldSched {
			if s.f.pass.debug > regDebug {
				fmt.Printf("  processing %s\n", v.LongString())
			}
			regspec := s.regspec(v)
			if v.Op == OpPhi {
				f.Fatalf("phi %s not at start of block", v)
			}
			if v.Op == OpSP {
				s.assignReg(s.SPReg, v, v)
				b.Values = append(b.Values, v)
				s.advanceUses(v)
				s.sp = v.ID
				continue
			}
			if v.Op == OpSB {
				s.assignReg(s.SBReg, v, v)
				b.Values = append(b.Values, v)
				s.advanceUses(v)
				s.sb = v.ID
				continue
			}
			if v.Op == OpSelect0 || v.Op == OpSelect1 || v.Op == OpSelectN {
				if s.values[v.ID].needReg {
					if v.Op == OpSelectN {
						s.assignReg(register(s.f.getHome(v.Args[0].ID).(LocResults)[int(v.AuxInt)].(*Register).num), v, v)
					} else {
						var i = 0
						if v.Op == OpSelect1 {
							i = 1
						}
						s.assignReg(register(s.f.getHome(v.Args[0].ID).(LocPair)[i].(*Register).num), v, v)
					}
				}
				b.Values = append(b.Values, v)
				s.advanceUses(v)
				goto issueSpill
			}
			if v.Op == OpGetG && s.f.Config.hasGReg {
				// 使用硬件g寄存器
				if s.regs[s.GReg].v != nil {
					s.freeReg(s.GReg) // 抛弃旧价值观
				}
				s.assignReg(s.GReg, v, v)
				b.Values = append(b.Values, v)
				s.advanceUses(v)
				goto issueSpill
			}
			if v.Op == OpArg {
				// arg是“预溢出”值。我们不分配
				// 这里有挂号吗。我们刚刚设置了指向的溢出指针
				// 指向它本身，任何以后的用户都将恢复它以使用它。
				s.values[v.ID].spill = v
				b.Values = append(b.Values, v)
				s.advanceUses(v)
				continue
			}
			if v.Op == OpKeepAlive {
				// 确保v的参数仍然在这里。
				s.advanceUses(v)
				a := v.Args[0]
				vi := &s.values[a.ID]
				if vi.regs == 0 && !vi.rematerializeable {
					// 使用泄漏位置。
					// 这迫使以后的活动性分析做出
					// 价值在这一点上是活的。
					v.SetArg(0, s.makeSpill(a, b))
				} else if _, ok := a.Aux.(*ir.Name); ok && vi.rematerializeable {
					// 具有gc.Node的可重物质化值。这是我的地址
					// 堆栈对象（例如LEAQ）。使对象保持活动状态。
					// 将其更改为VarLive，这是plive对本地人的期望。
					v.Op = OpVarLive
					v.SetArgs1(v.Args[1])
					v.Aux = a.Aux
				} else {
					// 寄存器中的和可重物质化的值已处于活动状态。
					// 这些通常是可重物质化的常数，如nil，
					// 或自上次调用以来修改的变量值。
					v.Op = OpCopy
					v.SetArgs1(v.Args[1])
				}
				b.Values = append(b.Values, v)
				continue
			}
			if len(regspec.inputs) == 0 && len(regspec.outputs) == 0 {
				// 不需要寄存器分配（或尚未指定）
				if s.doClobber && v.Op.IsCall() {
					s.clobberRegs(regspec.clobbers)
				}
				s.freeRegs(regspec.clobbers)
				b.Values = append(b.Values, v)
				s.advanceUses(v)
				continue
			}

			if s.values[v.ID].rematerializeable {
				// 值是可重物质化的，不要在此处发布。
				// 它将在每次使用前发布（参见
				// allocValueToReg）。
				for _, a := range v.Args {
					a.Uses--
				}
				s.advanceUses(v)
				continue
			}

			if s.f.pass.debug > regDebug {
				fmt.Printf("value %s\n", v.LongString())
				fmt.Printf("  out:")
				for _, r := range dinfo[idx].out {
					if r != noRegister {
						fmt.Printf(" %s", &s.registers[r])
					}
				}
				fmt.Println()
				for i := 0; i < len(v.Args) && i < 3; i++ {
					fmt.Printf("  in%d:", i)
					for _, r := range dinfo[idx].in[i] {
						if r != noRegister {
							fmt.Printf(" %s", &s.registers[r])
						}
					}
					fmt.Println()
				}
			}

			// 将参数移动到寄存器。
			// 首先，如果arg必须在特定寄存器中，并且它已经在
			// 在适当的地方，保持它。
			args = append(args[:0], make([]*Value, len(v.Args))...)
			for i, a := range v.Args {
				if !s.values[a.ID].needReg {
					args[i] = a
				}
			}
			for _, i := range regspec.inputs {
				mask := i.regs
				if countRegs(mask) == 1 && mask&s.values[v.Args[i.idx].ID].regs != 0 {
					args[i.idx] = s.allocValToReg(v.Args[i.idx], mask, true, v.Pos)
				}
			}
			// 然后，如果arg必须位于特定寄存器中
			// 注册是免费的，分配那个。否则在处理时
			// 另一个输入，我们可以将一个值踢入空闲寄存器，这
			// 然后又会被踢出去。
			// 这是传递的寄存器参数的常见情况
			// 函数调用。
			for {
				freed := false
				for _, i := range regspec.inputs {
					if args[i.idx] != nil {
						continue // 已分配
					}
					mask := i.regs
					if countRegs(mask) == 1 && mask&^s.used != 0 {
						args[i.idx] = s.allocValToReg(v.Args[i.idx], mask, true, v.Pos)
						// 如果输入在其他寄存器中，将被v关闭，
						// 或者输入无效，释放寄存器。这可能会腾出空间
						// 用于其他输入。
						oldregs := s.values[v.Args[i.idx].ID].regs
						if oldregs&^regspec.clobbers == 0 || !s.liveAfterCurrentInstruction(v.Args[i.idx]) {
							s.freeRegs(oldregs &^ mask &^ s.nospill)
							freed = true
						}
					}
				}
				if !freed {
					break
				}
			}
			// 最后，按照定义的顺序分配剩余的
			// 根据寄存器规范（最受约束的优先）。
			for _, i := range regspec.inputs {
				if args[i.idx] != nil {
					continue // 已分配
				}
				mask := i.regs
				if mask&s.values[v.Args[i.idx].ID].regs == 0 {
					// 输入需要一个新的寄存器。
					mask &= s.allocatable
					mask &^= s.nospill
					// 使用所需的寄存器（如果可用）。
					if i.idx < 3 {
						for _, r := range dinfo[idx].in[i.idx] {
							if r != noRegister && (mask&^s.used)>>r&1 != 0 {
								// 所需寄存器是允许的且未使用。
								mask = regMask(1) << r
								break
							}
						}
					}
					// 避免我们为其他值保存的寄存器。
					if mask&^desired.avoid != 0 {
						mask &^= desired.avoid
					}
				}
				args[i.idx] = s.allocValToReg(v.Args[i.idx], mask, true, v.Pos)
			}

			// 如果输出冲击输入寄存器，请确保
			// 至少有两份输入寄存器的副本，这样我们就不会
			// 必须从溢出位置重新加载值。
			if opcodeTable[v.Op].resultInArg0 {
				var m regMask
				if !s.liveAfterCurrentInstruction(v.Args[0]) {
					// arg0已经死了。我们可以重击它的寄存器。
					goto ok
				}
				if opcodeTable[v.Op].commutative && !s.liveAfterCurrentInstruction(v.Args[1]) {
					args[0], args[1] = args[1], args[0]
					goto ok
				}
				if s.values[v.Args[0].ID].rematerializeable {
					// 我们可以将输入重新物质化，不必担心会破坏它。
					goto ok
				}
				if opcodeTable[v.Op].commutative && s.values[v.Args[1].ID].rematerializeable {
					args[0], args[1] = args[1], args[0]
					goto ok
				}
				if countRegs(s.values[v.Args[0].ID].regs) >= 2 {
					// 我们至少有2份arg0。我们能负担得起重击一个。
					goto ok
				}
				if opcodeTable[v.Op].commutative && countRegs(s.values[v.Args[1].ID].regs) >= 2 {
					args[0], args[1] = args[1], args[0]
					goto ok
				}

				// 我们无法覆盖arg0（或arg1，如果是可交换的）。所以我们
				// 需要制作输入的副本，以便我们有一个可以修改的寄存器。

				// 可能要复制到的新寄存器。
				m = s.compatRegs(v.Args[0].Type) &^ s.used
				if m == 0 {
					// 没有免费注册。在这种情况下，我们只需重击
					// 输入和该输入的未来使用必须使用还原。
					// 托多（khr）：我们真的应该像阿洛克雷格那样做，
					// 将值溢出到最远的下一次使用。
					goto ok
				}

				// 如果允许，尝试将输入移动到所需的输出。
				for _, r := range dinfo[idx].out {
					if r != noRegister && (m&regspec.outputs[0].regs)>>r&1 != 0 {
						m = regMask(1) << r
						args[0] = s.allocValToReg(v.Args[0], m, true, v.Pos)
						// 注意：我们更新了args[0]，因此指令将
						// 使用我们刚刚制作的注册副本。
						goto ok
					}
				}
				// 尝试将输入复制到所需位置并使用其旧位置
				// 作为结果寄存器的位置。
				for _, r := range dinfo[idx].in[0] {
					if r != noRegister && m>>r&1 != 0 {
						m = regMask(1) << r
						c := s.allocValToReg(v.Args[0], m, true, v.Pos)
						s.copies[c] = false
						// 注意：args[0]没有更新，因此指令将
						// 使用原件。
						goto ok
					}
				}
				if opcodeTable[v.Op].commutative {
					for _, r := range dinfo[idx].in[1] {
						if r != noRegister && m>>r&1 != 0 {
							m = regMask(1) << r
							c := s.allocValToReg(v.Args[1], m, true, v.Pos)
							s.copies[c] = false
							args[0], args[1] = args[1], args[0]
							goto ok
						}
					}
				}
				// 尽可能避免将来的固定用途。
				if m&^desired.avoid != 0 {
					m &^= desired.avoid
				}
				// 将输入0保存到一个新的寄存器，以便我们可以删除它。
				c := s.allocValToReg(v.Args[0], m, true, v.Pos)
				s.copies[c] = false
			}

		ok:
			// 既然所有参数都在regs中，我们就准备好发布值本身了。
			// 在为输出值选择寄存器之前，请允许输入寄存器
			// 将被解除分配。我们在这里这样做是为了让输出可以使用
			// 与正在消亡的输入相同的寄存器。
			if !opcodeTable[v.Op].resultNotInArgs {
				s.tmpused = s.nospill
				s.nospill = 0
				s.advanceUses(v) // 释放包含不再有效参数的所有寄存器
			}

			// 转储任何将被删除的寄存器
			if s.doClobber && v.Op.IsCall() {
				// 在regmask中标记为clobber的clobber寄存器，但
				// 不要重击输入。
				s.clobberRegs(regspec.clobbers &^ s.tmpused &^ s.nospill)
			}
			s.freeRegs(regspec.clobbers)
			s.tmpused |= regspec.clobbers

			// 选择输出寄存器。
			{
				outRegs := noRegisters // TODO如果成本高昂，请在下方以增量方式提升和清除。
				maxOutIdx := -1
				var used regMask
				for _, out := range regspec.outputs {
					mask := out.regs & s.allocatable &^ used
					if mask == 0 {
						continue
					}
					if opcodeTable[v.Op].resultInArg0 && out.idx == 0 {
						if !opcodeTable[v.Op].commutative {
							// 输出必须使用与输入0相同的寄存器。
							r := register(s.f.getHome(args[0].ID).(*Register).num)
							if mask>>r&1 == 0 {
								s.f.Fatalf("resultInArg0 value's input %v cannot be an output of %s", s.f.getHome(args[0].ID).(*Register), v.LongString())
							}
							mask = regMask(1) << r
						} else {
							// 输出必须使用与输入0或1相同的寄存器。
							r0 := register(s.f.getHome(args[0].ID).(*Register).num)
							r1 := register(s.f.getHome(args[1].ID).(*Register).num)
							// 检查r0和r1是否有所需的输出寄存器。
							found := false
							for _, r := range dinfo[idx].out {
								if (r == r0 || r == r1) && (mask&^s.used)>>r&1 != 0 {
									mask = regMask(1) << r
									found = true
									if r == r1 {
										args[0], args[1] = args[1], args[0]
									}
									break
								}
							}
							if !found {
								// 两者都不需要，请选择r0。
								mask = regMask(1) << r0
							}
						}
					}
					for _, r := range dinfo[idx].out {
						if r != noRegister && (mask&^s.used)>>r&1 != 0 {
							// 所需寄存器是允许的且未使用。
							mask = regMask(1) << r
							break
						}
					}
					// 避免我们为其他值保存的寄存器。
					if mask&^desired.avoid&^s.nospill != 0 {
						mask &^= desired.avoid
					}
					r := s.allocReg(mask, v)
					if out.idx > maxOutIdx {
						maxOutIdx = out.idx
					}
					outRegs[out.idx] = r
					used |= regMask(1) << r
					s.tmpused |= regMask(1) << r
				}
				// 记录寄存器选择
				if v.Type.IsTuple() {
					var outLocs LocPair
					if r := outRegs[0]; r != noRegister {
						outLocs[0] = &s.registers[r]
					}
					if r := outRegs[1]; r != noRegister {
						outLocs[1] = &s.registers[r]
					}
					s.f.setHome(v, outLocs)
					// 请注意，随后的SelectX指令将执行assignReg调用。
				} else if v.Type.IsResults() {
					// 将Outloc预分配到正确的大小，即maxOutIdx+1
					outLocs := make(LocResults, maxOutIdx+1, maxOutIdx+1)
					for i := 0; i <= maxOutIdx; i++ {
						if r := outRegs[i]; r != noRegister {
							outLocs[i] = &s.registers[r]
						}
					}
					s.f.setHome(v, outLocs)
				} else {
					if r := outRegs[0]; r != noRegister {
						s.assignReg(r, v, v)
					}
				}
			}

			// 如果我们没有这样做，则取消分配死args
			if opcodeTable[v.Op].resultNotInArgs {
				s.nospill = 0
				s.advanceUses(v) // 释放包含不再有效参数的所有寄存器
			}
			s.tmpused = 0

			// 发行价值本身。
			for i, a := range args {
				v.SetArg(i, a) // 使用参数的寄存器版本
			}
			b.Values = append(b.Values, v)

		issueSpill:
		}

		// 复制控制值-我们需要它，以便减少
		// 稍后使用这些值的属性。
		controls := append(make([]*Value, 0, 2), b.ControlValues()...)

		// 将控制值加载到寄存器中。
		for i, v := range b.ControlValues() {
			if !s.values[v.ID].needReg {
				continue
			}
			if s.f.pass.debug > regDebug {
				fmt.Printf("  processing control %s\n", v.LongString())
			}
			// 我们假设一个控制输入可以以任何方式传递
			// 类型兼容寄存器。如果这不是真的，
			// 我们需要为块的控制值引入一个regspec。
			b.ReplaceControl(i, s.allocValToReg(v, s.compatRegs(v.Type), false, b.Pos))
		}

		// 加载寄存器后，减少控制值的使用。
		// 此循环相当于advanceUses方法。
		for _, v := range controls {
			vi := &s.values[v.ID]
			if !vi.needReg {
				continue
			}
			// 从“用途”列表中删除此用途。
			u := vi.uses
			vi.uses = u.next
			if u.next == nil {
				s.freeRegs(vi.regs) // 价值死了
			}
			u.next = s.freeUseRecords
			s.freeUseRecords = u
		}

		// 如果我们接近一个合并点并且我们是主要的
		// 它的前身，找到我们不久后使用的实时值
		// 合并点并立即将其升级到注册表。
		if len(b.Succs) == 1 {
			if s.f.Config.hasGReg && s.regs[s.GReg].v != nil {
				s.freeReg(s.GReg) // 在任何合并之前，溢出G寄存器中的值。
			}
			// 为了使这项工作有价值，循环中必须没有调用。
			top := b.Succs[0].b
			loop := s.loopnest.b2l[top.ID]
			if loop == nil || loop.header != top || loop.containsUnavoidableCall {
				goto badloop
			}

			// TODO:按距离排序，选择最近的？
			for _, live := range s.live[b.ID] {
				if live.dist >= unlikelyDistance {
					// 不要在循环后预加载任何活动内容。
					continue
				}
				vid := live.ID
				vi := &s.values[vid]
				if vi.regs != 0 {
					continue
				}
				if vi.rematerializeable {
					continue
				}
				v := s.orig[vid]
				m := s.compatRegs(v.Type) &^ s.used
				// 使用所需的寄存器（如果可用）。
			outerloop:
				for _, e := range desired.entries {
					if e.ID != v.ID {
						continue
					}
					for _, r := range e.regs {
						if r != noRegister && m>>r&1 != 0 {
							m = regMask(1) << r
							break outerloop
						}
					}
				}
				if m&^desired.avoid != 0 {
					m &^= desired.avoid
				}
				if m != 0 {
					s.allocValToReg(v, m, false, b.Pos)
				}
			}
		}
	badloop:
		;

		// 保存块寄存器的结束状态。
		// 首先计算一下有多少，这会将拨款削减一半。
		k := 0
		for r := register(0); r < s.numRegs; r++ {
			v := s.regs[r].v
			if v == nil {
				continue
			}
			k++
		}
		regList := make([]endReg, 0, k)
		for r := register(0); r < s.numRegs; r++ {
			v := s.regs[r].v
			if v == nil {
				continue
			}
			regList = append(regList, endReg{r, v, s.regs[r].c})
		}
		s.endRegs[b.ID] = regList

		if checkEnabled {
			regValLiveSet.clear()
			for _, x := range s.live[b.ID] {
				regValLiveSet.add(x.ID)
			}
			for r := register(0); r < s.numRegs; r++ {
				v := s.regs[r].v
				if v == nil {
					continue
				}
				if !regValLiveSet.contains(v.ID) {
					s.f.Fatalf("val %s is in reg but not live at end of %s", v, b)
				}
			}
		}

		// 如果某个值在块的末尾处于活动状态，则
		// 不在寄存器中，生成溢出位置的使用。
		// 我们需要记住这些信息以便
		// stackalloc中的活性分析是正确的。
		for _, e := range s.live[b.ID] {
			vi := &s.values[e.ID]
			if vi.regs != 0 {
				// 在寄存器中，我们将使用该源进行合并。
				continue
			}
			if vi.rematerializeable {
				// 我们将在合并期间重新实现材质化。
				continue
			}
			if s.f.pass.debug > regDebug {
				fmt.Printf("live-at-end spill for %s at %s\n", s.orig[e.ID], b)
			}
			spill := s.makeSpill(s.orig[e.ID], b)
			s.spillLive[b.ID] = append(s.spillLive[b.ID], spill.ID)
		}

		// 清除任何最终用途。
		// 剩下的应该是为以下值添加的伪uses
		// 我们住在b区的尽头。
		for _, e := range s.live[b.ID] {
			u := s.values[e.ID].uses
			if u == nil {
				f.Fatalf("live at end, no uses v%d", e.ID)
			}
			if u.next != nil {
				f.Fatalf("live at end, too many uses v%d", e.ID)
			}
			s.values[e.ID].uses = nil
			u.next = s.freeUseRecords
			s.freeUseRecords = u
		}
	}

	// 决定我们产生的泄漏将流向何处。
	s.placeSpills()

	// 任何没有得到寄存器的东西都会在这里得到堆栈位置。
	// （StoreReg、基于堆栈的PHI、输入等）
	stacklive := stackalloc(s.f, s.spillLive)

	// 修复所有合并边。
	s.shuffle(stacklive)

	// 删除我们从未使用过的任何副本。
	// 另外，未使用的副本可能是另一个副本的唯一用途，
	// 所以继续擦除直到我们到达一个固定点。
	for {
		progress := false
		for c, used := range s.copies {
			if !used && c.Uses == 0 {
				if s.f.pass.debug > regDebug {
					fmt.Printf("delete copied value %s\n", c.LongString())
				}
				c.RemoveArg(0)
				f.freeValue(c)
				delete(s.copies, c)
				progress = true
			}
		}
		if !progress {
			break
		}
	}

	for _, b := range s.visitOrder {
		i := 0
		for _, v := range b.Values {
			if v.Op == OpInvalid {
				continue
			}
			b.Values[i] = v
			i++
		}
		b.Values = b.Values[:i]
	}
}

func (s *regAllocState) placeSpills() {
	f := s.f

	// 预先计算一些有用的信息。
	phiRegs := make([]regMask, f.NumBlocks())
	for _, b := range s.visitOrder {
		var m regMask
		for _, v := range b.Values {
			if v.Op != OpPhi {
				break
			}
			if r, ok := f.getHome(v.ID).(*Register); ok {
				m |= regMask(1) << uint(r.num)
			}
		}
		phiRegs[b.ID] = m
	}

	mustBeFirst := func(op Op) bool {
		return op.isLoweredGetClosurePtr() || op == OpPhi || op == OpArgIntReg || op == OpArgFloatReg
	}

	// 开始将块ID映射到溢出列表
	// 在区块开始时进行（但在任何潜在危险装置之后）。
	start := map[ID][]*Value{}
	// 之后将值ID映射到溢出列表
	// 它紧跟在该值ID之后。
	after := map[ID][]*Value{}

	for i := range s.values {
		vi := s.values[i]
		spill := vi.spill
		if spill == nil {
			continue
		}
		if spill.Block != nil {
			// 一些泄漏已经完全设置好，
			// 像OpArgs和基于堆栈的phis。
			continue
		}
		v := s.orig[i]

		// 沿着支配树走下去，寻找一个好地方
		// 把溢出的v。一开始，“最好的”是最好的地方
		// 到目前为止，我们已经找到了。
		// TODO:找到一种方法使此O（1）无需任意截断。
		if v == nil {
			panic(fmt.Errorf("nil v, s.orig[%d], vi = %v, spill = %s", i, vi, spill.LongString()))
		}
		best := v.Block
		bestArg := v
		var bestDepth int16
		if l := s.loopnest.b2l[best.ID]; l != nil {
			bestDepth = l.depth
		}
		b := best
		const maxSpillSearch = 100
		for i := 0; i < maxSpillSearch; i++ {
			// 在支配树中找到b的子元素
			// 主宰一切恢复。
			p := b
			b = nil
			for c := s.sdom.Child(p); c != nil && i < maxSpillSearch; c, i = s.sdom.Sibling(c), i+1 {
				if s.sdom[c.ID].entry <= vi.restoreMin && s.sdom[c.ID].exit >= vi.restoreMax {
					// c也支配着所有的恢复。走进c。
					b = c
					break
				}
			}
			if b == nil {
				// 耗尽了主导所有恢复的块。
				break
			}

			var depth int16
			if l := s.loopnest.b2l[b.ID]; l != nil {
				depth = l.depth
			}
			if depth > bestDepth {
				// 不要将溢出物推入更深的循环。
				continue
			}

			// 如果v在b开头的寄存器中，我们可以
			// 将溢出物放在此处（在潜在危险装置之后）。
			if len(b.Preds) == 1 {
				for _, e := range s.endRegs[b.Preds[0].b.ID] {
					if e.v == v {
						// 找到了更好的泄漏点。
						best = b
						bestArg = e.c
						bestDepth = depth
						break
					}
				}
			} else {
				for _, e := range s.startRegs[b.ID] {
					if e.v == v {
						// 找到了更好的泄漏点。
						best = b
						bestArg = e.c
						bestDepth = depth
						break
					}
				}
			}
		}

		// 把溢油放在我们找到的最好的地方。
		spill.Block = best
		spill.AddArg(bestArg)
		if best == v.Block && !mustBeFirst(v.Op) {
			// 紧接着放在v之后。
			after[v.ID] = append(after[v.ID], spill)
		} else {
			// 放置在最佳块的开头。
			start[best.ID] = append(start[best.ID], spill)
		}
	}

	// 在块明细表中插入溢出说明。
	var oldSched []*Value
	for _, b := range s.visitOrder {
		nfirst := 0
		for _, v := range b.Values {
			if !mustBeFirst(v.Op) {
				break
			}
			nfirst++
		}
		oldSched = append(oldSched[:0], b.Values[nfirst:]...)
		b.Values = b.Values[:nfirst]
		b.Values = append(b.Values, start[b.ID]...)
		for _, v := range oldSched {
			b.Values = append(b.Values, v)
			b.Values = append(b.Values, after[v.ID]...)
		}
	}
}

// shuffle修复所有合并边（那些进入indegree>1块的边）。
func (s *regAllocState) shuffle(stacklive [][]ID) {
	var e edgeState
	e.s = s
	e.cache = map[ID][]*Value{}
	e.contents = map[Location]contentRecord{}
	if s.f.pass.debug > regDebug {
		fmt.Printf("shuffle %s\n", s.f.Name)
		fmt.Println(s.f.String())
	}

	for _, b := range s.visitOrder {
		if len(b.Preds) <= 1 {
			continue
		}
		e.b = b
		for i, edge := range b.Preds {
			p := edge.b
			e.p = p
			e.setup(i, s.endRegs[p.ID], s.startRegs[b.ID], stacklive[p.ID])
			e.process()
		}
	}

	if s.f.pass.debug > regDebug {
		fmt.Printf("post shuffle %s\n", s.f.Name)
		fmt.Println(s.f.String())
	}
}

type edgeState struct {
	s    *regAllocState
	p, b *Block // 边从p->b开始。

	// 对于每个pre-regalloc值，一个等效缓存值列表
	cache      map[ID][]*Value
	cachedVals []ID // 上述地图的（超集）键，用于确定性迭代

	// 从位置映射到它包含的值
	contents map[Location]contentRecord

	// 所需目的地位置
	destinations []dstRecord
	extra        []dstRecord

	usedRegs              regMask // 当前持有某物的登记簿
	uniqueRegs            regMask // 保存值的唯一副本的寄存器
	finalRegs             regMask // 保持最终目标的寄存器
	rematerializeableRegs regMask // 保存可重物质化值的寄存器
}

type contentRecord struct {
	vid   ID       // 前regalloc值
	c     *Value   // 缓存值
	final bool     // 这是一个令人满意的目的地
	pos   src.XPos // 值使用的源位置
}

type dstRecord struct {
	loc    Location // 寄存器或堆栈插槽
	vid    ID       // 它应该包含的pre-regalloc值
	splice **Value  // 存储对生成指令的引用的位置
	pos    src.XPos // 使用此位置的源位置
}

// 安装程序初始化洗牌的边缘状态。
func (e *edgeState) setup(idx int, srcReg []endReg, dstReg []startReg, stacklive []ID) {
	if e.s.f.pass.debug > regDebug {
		fmt.Printf("edge %s->%s\n", e.p, e.b)
	}

	// 清除状态。
	for _, vid := range e.cachedVals {
		delete(e.cache, vid)
	}
	e.cachedVals = e.cachedVals[:0]
	for k := range e.contents {
		delete(e.contents, k)
	}
	e.usedRegs = 0
	e.uniqueRegs = 0
	e.finalRegs = 0
	e.rematerializeableRegs = 0

	// 实时寄存器可以是源。
	for _, x := range srcReg {
		e.set(&e.s.registers[x.r], x.v.ID, x.c, false, src.NoXPos) // 不要在意震源的位置
	}
	// 所有的泄漏地点也是如此。
	for _, spillID := range stacklive {
		v := e.s.orig[spillID]
		spill := e.s.values[v.ID].spill
		if !e.s.sdom.IsAncestorEq(spill.Block, e.p) {
			// 溢出物的放置仅在发现的用途中占主导地位
			// 在第一次regalloc通行证期间。边缘修正码
			// 如果溢出不占主导地位，则不能使用溢出位置
			// 边缘。
			// 我们可以保证，如果泄漏不占优势，
			// 然后该值在寄存器中可用（因为我们调用
			// 开始时不在寄存器中的每个值的makeSpill
			// （指边缘）。
			continue
		}
		e.set(e.s.f.getHome(spillID), v.ID, spill, false, src.NoXPos) // 不要在意震源的位置
	}

	// 找出我们需要的所有目的地。
	dsts := e.destinations[:0]
	for _, x := range dstReg {
		dsts = append(dsts, dstRecord{&e.s.registers[x.r], x.v.ID, nil, x.pos})
	}
	// PhI需要将其arg放在特定位置。
	for _, v := range e.b.Values {
		if v.Op != OpPhi {
			break
		}
		loc := e.s.f.getHome(v.ID)
		if loc == nil {
			continue
		}
		dsts = append(dsts, dstRecord{loc, v.Args[idx].ID, &v.Args[idx], v.Pos})
	}
	e.destinations = dsts

	if e.s.f.pass.debug > regDebug {
		for _, vid := range e.cachedVals {
			a := e.cache[vid]
			for _, c := range a {
				fmt.Printf("src %s: v%d cache=%s\n", e.s.f.getHome(c.ID), vid, c)
			}
		}
		for _, d := range e.destinations {
			fmt.Printf("dst %s: v%d\n", d.loc, d.vid)
		}
	}
}

// 进程生成代码，将所有值移动到正确的目标位置。
func (e *edgeState) process() {
	dsts := e.destinations

	// 处理目的地，直到他们都满意为止。
	for len(dsts) > 0 {
		i := 0
		for _, d := range dsts {
			if !e.processDest(d.loc, d.vid, d.splice, d.pos) {
				// 失败-为下一次迭代保存。
				dsts[i] = d
				i++
			}
		}
		if i < len(dsts) {
			// 取得了一些进展。再转一圈。
			dsts = dsts[:i]

			// 附加我们生成的任何附加目的地。
			dsts = append(dsts, e.extra...)
			e.extra = e.extra[:0]
			continue
		}

		// 我们没有取得任何进展。这意味着任何
		// 剩下的不满意的动作是简单的循环。
		// 例如，A->B->C->D->A。
		// A--->B
		// ^       |
		// |       |
		// |五
		// D<--C

		// 为了打破这个循环，我们选择一个未使用的寄存器，比如R，
		// 然后在那里放一份B。
		// A--->B
		// ^       |
		// |       |
		// |五
		// D<--C<--R=copyofB
		// 当我们恢复外循环时，A->B移动现在可以继续，
		// 最终整个循环完成。

		// 将任何循环位置复制到临时寄存器。这是重复的
		// 循环条目之一，允许刚刚复制的值
		// 将被覆盖，循环将继续。
		d := dsts[0]
		loc := d.loc
		vid := e.contents[loc].vid
		c := e.contents[loc].c
		r := e.findRegFor(c.Type)
		if e.s.f.pass.debug > regDebug {
			fmt.Printf("breaking cycle with v%d in %s:%s\n", vid, loc, c)
		}
		e.erase(r)
		pos := d.pos.WithNotStmt()
		if _, isReg := loc.(*Register); isReg {
			c = e.p.NewValue1(pos, OpCopy, c.Type, c)
		} else {
			c = e.p.NewValue1(pos, OpLoadReg, c.Type, c)
		}
		e.set(r, vid, c, false, pos)
		if c.Op == OpLoadReg && e.s.isGReg(register(r.(*Register).num)) {
			e.s.f.Fatalf("process.OpLoadReg targeting g: " + c.LongString())
		}
	}
}

// processDest生成将值vid放入位置loc的代码。返回true
// 如果取得了进展。
func (e *edgeState) processDest(loc Location, vid ID, splice **Value, pos src.XPos) bool {
	pos = pos.WithNotStmt()
	occupant := e.contents[loc]
	if occupant.vid == vid {
		// 值已位于正确的位置。
		e.contents[loc] = contentRecord{vid, occupant.c, true, pos}
		if splice != nil {
			(*splice).Uses--
			*splice = occupant.c
			occupant.c.Uses++
		}
		// 注：如果拼接==nil，则c将显示为死区。这是
		// 非SSA格式的代码，因此在传递后请小心不要运行
		// 死码消除。
		if _, ok := e.s.copies[occupant.c]; ok {
			// 乘员c处的副本用于避免泄漏。
			e.s.copies[occupant.c] = true
		}
		return true
	}

	// 检查我们是否被允许在目的地扫荡。
	if len(e.cache[occupant.vid]) == 1 && !e.s.values[occupant.vid].rematerializeable {
		// 我们无法覆盖最后一份
		// 一种需要生存的价值。
		return false
	}

	// 从v源复制，首选寄存器。
	v := e.s.orig[vid]
	var c *Value
	var src Location
	if e.s.f.pass.debug > regDebug {
		fmt.Printf("moving v%d to %s\n", vid, loc)
		fmt.Printf("sources of v%d:", vid)
	}
	for _, w := range e.cache[vid] {
		h := e.s.f.getHome(w.ID)
		if e.s.f.pass.debug > regDebug {
			fmt.Printf(" %s:%s", h, w)
		}
		_, isreg := h.(*Register)
		if src == nil || isreg {
			c = w
			src = h
		}
	}
	if e.s.f.pass.debug > regDebug {
		if src != nil {
			fmt.Printf(" [use %s]\n", src)
		} else {
			fmt.Printf(" [no source]\n")
		}
	}
	_, dstReg := loc.(*Register)

	// 预碰撞目标。这避免了
	// 以下情况：
	// -v目前保存在R0和stacktmp0中。
	// -我们要将stacktmp1复制到stacktmp0。
	// -我们选择R0作为临时寄存器。
	// 在复制过程中，R0和stacktmp0都是
	// 撞坏了，丢失了v的两份副本。哎呀！
	// 提前擦除目标意味着R0将不会
	// 被选为临时寄存器，因为它将
	// 是v的最后一份。
	e.erase(loc)
	var x *Value
	if c == nil || e.s.values[vid].rematerializeable {
		if !e.s.values[vid].rematerializeable {
			e.s.f.Fatalf("can't find source for %s->%s: %s\n", e.p, e.b, v.LongString())
		}
		if dstReg {
			x = v.copyInto(e.p)
		} else {
			// 重新材质到堆栈插槽中。需要一个免费的
			// 注册以完成此任务。
			r := e.findRegFor(v.Type)
			e.erase(r)
			x = v.copyIntoWithXPos(e.p, pos)
			e.set(r, vid, x, false, pos)
			// 确保我们洒的是槽的大小，而不是槽的大小
			// x的大小（由于我们的下降可能会更宽）
			// 缩小转换范围）。
			x = e.p.NewValue1(pos, OpStoreReg, loc.(LocalSlot).Type, x)
		}
	} else {
		// 发射从src移动到dst。
		_, srcReg := src.(*Register)
		if srcReg {
			if dstReg {
				x = e.p.NewValue1(pos, OpCopy, c.Type, c)
			} else {
				x = e.p.NewValue1(pos, OpStoreReg, loc.(LocalSlot).Type, c)
			}
		} else {
			if dstReg {
				x = e.p.NewValue1(pos, OpLoadReg, c.Type, c)
			} else {
				// 记忆->记忆。使用临时寄存器。
				r := e.findRegFor(c.Type)
				e.erase(r)
				t := e.p.NewValue1(pos, OpLoadReg, c.Type, c)
				e.set(r, vid, t, false, pos)
				x = e.p.NewValue1(pos, OpStoreReg, loc.(LocalSlot).Type, t)
			}
		}
	}
	e.set(loc, vid, x, true, pos)
	if x.Op == OpLoadReg && e.s.isGReg(register(loc.(*Register).num)) {
		e.s.f.Fatalf("processDest.OpLoadReg targeting g: " + x.LongString())
	}
	if splice != nil {
		(*splice).Uses--
		*splice = x
		x.Uses++
	}
	return true
}

// set更改location loc的内容以保存给定值及其缓存的代表。
func (e *edgeState) set(loc Location, vid ID, c *Value, final bool, pos src.XPos) {
	e.s.f.setHome(c, loc)
	e.contents[loc] = contentRecord{vid, c, final, pos}
	a := e.cache[vid]
	if len(a) == 0 {
		e.cachedVals = append(e.cachedVals, vid)
	}
	a = append(a, c)
	e.cache[vid] = a
	if r, ok := loc.(*Register); ok {
		if e.usedRegs&(regMask(1)<<uint(r.num)) != 0 {
			e.s.f.Fatalf("%v is already set (v%d/%v)", r, vid, c)
		}
		e.usedRegs |= regMask(1) << uint(r.num)
		if final {
			e.finalRegs |= regMask(1) << uint(r.num)
		}
		if len(a) == 1 {
			e.uniqueRegs |= regMask(1) << uint(r.num)
		}
		if len(a) == 2 {
			if t, ok := e.s.f.getHome(a[0].ID).(*Register); ok {
				e.uniqueRegs &^= regMask(1) << uint(t.num)
			}
		}
		if e.s.values[vid].rematerializeable {
			e.rematerializeableRegs |= regMask(1) << uint(r.num)
		}
	}
	if e.s.f.pass.debug > regDebug {
		fmt.Printf("%s\n", c.LongString())
		fmt.Printf("v%d now available in %s:%s\n", vid, loc, c)
	}
}

// 擦除删除loc的任何用户。
func (e *edgeState) erase(loc Location) {
	cr := e.contents[loc]
	if cr.c == nil {
		return
	}
	vid := cr.vid

	if cr.final {
		// 添加目标以将此值移回原位。
		// 确保将其添加到目标队列的尾部
		// 所以我们先在其他方面取得进展。
		e.extra = append(e.extra, dstRecord{loc, cr.vid, nil, cr.pos})
	}

	// 从缓存值列表中删除c。
	a := e.cache[vid]
	for i, c := range a {
		if e.s.f.getHome(c.ID) == loc {
			if e.s.f.pass.debug > regDebug {
				fmt.Printf("v%d no longer available in %s:%s\n", vid, loc, c)
			}
			a[i], a = a[len(a)-1], a[:len(a)-1]
			break
		}
	}
	e.cache[vid] = a

	// 更新寄存器掩码。
	if r, ok := loc.(*Register); ok {
		e.usedRegs &^= regMask(1) << uint(r.num)
		if cr.final {
			e.finalRegs &^= regMask(1) << uint(r.num)
		}
		e.rematerializeableRegs &^= regMask(1) << uint(r.num)
	}
	if len(a) == 1 {
		if r, ok := e.s.f.getHome(a[0].ID).(*Register); ok {
			e.uniqueRegs |= regMask(1) << uint(r.num)
		}
	}
}

// findRegFor查找一个寄存器，我们可以使用它来创建typ类型的临时副本。
func (e *edgeState) findRegFor(typ *types.Type) Location {
	// 哪些寄存器是可能的。
	types := &e.s.f.Config.Types
	m := e.s.compatRegs(typ)

	// 选择一个寄存器。按优先顺序：
	// 1） 未使用的寄存器
	// 2） 不包含最终值的非唯一寄存器
	// 3） 非唯一寄存器
	// 4） 保存可重物质化值的寄存器
	x := m &^ e.usedRegs
	if x != 0 {
		return &e.s.registers[pickReg(x)]
	}
	x = m &^ e.uniqueRegs &^ e.finalRegs
	if x != 0 {
		return &e.s.registers[pickReg(x)]
	}
	x = m &^ e.uniqueRegs
	if x != 0 {
		return &e.s.registers[pickReg(x)]
	}
	x = m & e.rematerializeableRegs
	if x != 0 {
		return &e.s.registers[pickReg(x)]
	}

	// 没有可用的注册表。
	// 选择一个寄存器溢出。
	for _, vid := range e.cachedVals {
		a := e.cache[vid]
		for _, c := range a {
			if r, ok := e.s.f.getHome(c.ID).(*Register); ok && m>>uint(r.num)&1 != 0 {
				if !c.rematerializeable() {
					x := e.p.NewValue1(c.Pos, OpStoreReg, c.Type, c)
					// 分配临时位置以将寄存器溢出到。
					// 插槽的类型无关紧要-它将不会在另一端活动
					// 任何安全点。只需使用一个足够大的类型来容纳任何寄存器。
					t := LocalSlot{N: e.s.f.fe.Auto(c.Pos, types.Int64), Type: types.Int64}
					// TODO:重用这些插槽。首先需要删除它们。
					e.set(t, vid, x, false, c.Pos)
					if e.s.f.pass.debug > regDebug {
						fmt.Printf("  SPILL %s->%s %s\n", r, t, x.LongString())
					}
				}
				// r现在将被调用方覆盖。在某个时候
				// 稍后，新保存的值将移回其位置
				// processDest中的最终目的地。
				return r
			}
		}
	}

	fmt.Printf("m:%d unique:%d final:%d rematerializable:%d\n", m, e.uniqueRegs, e.finalRegs, e.rematerializeableRegs)
	for _, vid := range e.cachedVals {
		a := e.cache[vid]
		for _, c := range a {
			fmt.Printf("v%d: %s %s\n", vid, c, e.s.f.getHome(c.ID))
		}
	}
	e.s.f.Fatalf("can't find empty register on edge %s->%s", e.p, e.b)
	return nil
}

// ReMaterializable报告寄存器分配器是否应重新计算
// 一个值，而不是溢出/恢复它。
func (v *Value) rematerializeable() bool {
	if !opcodeTable[v.Op].rematerializeable {
		return false
	}
	for _, a := range v.Args {
		// SP和SB（由OpSP和OpSB生成）始终可用。
		if a.Op != OpSP && a.Op != OpSB {
			return false
		}
	}
	return true
}

type liveInfo struct {
	ID   ID       // 价值ID
	dist int32    // #下一次使用前的使用说明
	pos  src.XPos // 下次使用的源位置
}

// computeLive计算从块ID到最后活动的值ID列表的映射
// 从那个街区出去。与值ID一起是多少条指令的计数
// 下一次使用该值。生成的地图存储在s.live中。
// computeLive还在每个块的末尾计算所需的寄存器信息。
// 该期望寄存器信息存储在s.desired中。
// TODO：如果大量变量在大量变量之间存在，那么这可能是二次的
// 基本块。找出一种实现此功能的方法（或者更准确地说，是用户
// 此函数的长度）只需要线性大小和时间。
func (s *regAllocState) computeLive() {
	f := s.f
	s.live = make([][]liveInfo, f.NumBlocks())
	s.desired = make([]desiredState, f.NumBlocks())
	var phis []*Value

	live := f.newSparseMap(f.NumValues())
	defer f.retSparseMap(live)
	t := f.newSparseMap(f.NumValues())
	defer f.retSparseMap(t)

	// 跟踪每个寄存器中需要的值。
	var desired desiredState

	// 与其迭代f.块，不如迭代它们的后序。
	// 活跃度信息向后流动，因此从末尾开始
	// 增加我们快速稳定的可能性。
	// 托多：做得更好。这里有一种可能性：
	// 计算支配树并定位所有强连接组件。
	// 如果某个值存在于SCC的一个块中，则该值存在于所有块中。
	// 从头到尾走支配树一次，治疗SCC
	// 组件作为单个块，重复计算的活动度信息
	// 向他们所有人说。
	po := f.postorder()
	s.loopnest = f.loopnest()
	s.loopnest.calculateDepths()
	for {
		changed := false

		for _, b := range po {
			// 从块末尾的已知活动值开始。
			// 添加len（b值）以从块的末端距离进行调整
			// 到区块距离的起点。
			live.clear()
			for _, e := range s.live[b.ID] {
				live.set(e.ID, e.dist+int32(len(b.Values)), e.pos)
			}

			// 将控件值标记为活动
			for _, c := range b.ControlValues() {
				if s.values[c.ID].needReg {
					live.set(c.ID, int32(len(b.Values)), b.Pos)
				}
			}

			// 向后传播到块的开头
			// 假设值已计划。
			phis = phis[:0]
			for i := len(b.Values) - 1; i >= 0; i-- {
				v := b.Values[i]
				live.remove(v.ID)
				if v.Op == OpPhi {
					// 保存phi操作以备以后使用
					phis = append(phis, v)
					continue
				}
				if opcodeTable[v.Op].call {
					c := live.contents()
					for i := range c {
						c[i].val += unlikelyDistance
					}
				}
				for _, a := range v.Args {
					if s.values[a.ID].needReg {
						live.set(a.ID, int32(i), v.Pos)
					}
				}
			}
			// 向后传播所需的寄存器。
			desired.copy(&s.desired[b.ID])
			for i := len(b.Values) - 1; i >= 0; i-- {
				v := b.Values[i]
				prefs := desired.remove(v.ID)
				if v.Op == OpPhi {
					// TODO：如果v是一个phi，保存phi输入所需的寄存器。
					// 现在，我们只是放弃它，不传播
					// 通过phi节点返回所需的寄存器。
					continue
				}
				regspec := s.regspec(v)
				// 如果所需寄存器被阻塞，则取消它们。
				desired.clobber(regspec.clobbers)
				// 如果存在任何固定寄存器输入，则更新所需寄存器。
				for _, j := range regspec.inputs {
					if countRegs(j.regs) != 1 {
						continue
					}
					desired.clobber(j.regs)
					desired.add(v.Args[j.idx].ID, pickReg(j.regs))
				}
				// 如果输入0是2操作数指令，则设置所需的寄存器。
				if opcodeTable[v.Op].resultInArg0 {
					if opcodeTable[v.Op].commutative {
						desired.addList(v.Args[1].ID, prefs)
					}
					desired.addList(v.Args[0].ID, prefs)
				}
			}

			// 对于b的每个前置项，展开其live at end值列表。
			// 不变：live包含b开头的live值（不包括phi输入）
			for i, e := range b.Preds {
				p := e.b
				// 计算边的附加距离。
				// None
				// 后继块中第一个用户使用的值。
				delta := int32(normalDistance)
				if len(p.Succs) == 2 {
					if p.Succs[0].b == b && p.Likely == BranchLikely ||
						p.Succs[1].b == b && p.Likely == BranchUnlikely {
						delta = likelyDistance
					}
					if p.Succs[0].b == b && p.Likely == BranchUnlikely ||
						p.Succs[1].b == b && p.Likely == BranchLikely {
						delta = unlikelyDistance
					}
				}

				// 在第页末尾更新任何所需的寄存器。
				s.desired[p.ID].merge(&desired)

				// 以p末尾之前已知的有效值开始t。
				t.clear()
				for _, e := range s.live[p.ID] {
					t.set(e.ID, e.dist, e.pos)
				}
				update := false

				// 通过扫描此块添加新的活动值。
				for _, e := range live.contents() {
					d := e.val + delta
					if !t.contains(e.key) || d < t.get(e.key) {
						update = true
						t.set(e.key, d, e.aux)
					}
				}
				// 还可以从保存的phi值中添加正确的参数。
				// 所有PHI都在距离δ（我们考虑它们）
				// 同时发生在块的开头）。
				for _, v := range phis {
					id := v.Args[i].ID
					if s.values[id].needReg && (!t.contains(id) || delta < t.get(id)) {
						update = true
						t.set(id, delta, v.Pos)
					}
				}

				if !update {
					continue
				}
				// 活动集已更改，请更新它。
				l := s.live[p.ID][:0]
				if cap(l) < t.size() {
					l = make([]liveInfo, 0, t.size())
				}
				for _, e := range t.contents() {
					l = append(l, liveInfo{e.key, e.val, e.aux})
				}
				s.live[p.ID] = l
				changed = true
			}
		}

		if !changed {
			break
		}
	}
	if f.pass.debug > regDebug {
		fmt.Println("live values at end of each block")
		for _, b := range f.Blocks {
			fmt.Printf("  %s:", b)
			for _, x := range s.live[b.ID] {
				fmt.Printf(" v%d(%d)", x.ID, x.dist)
				for _, e := range s.desired[b.ID].entries {
					if e.ID != x.ID {
						continue
					}
					fmt.Printf("[")
					first := true
					for _, r := range e.regs {
						if r == noRegister {
							continue
						}
						if !first {
							fmt.Printf(",")
						}
						fmt.Print(&s.registers[r])
						first = false
					}
					fmt.Printf("]")
				}
			}
			if avoid := s.desired[b.ID].avoid; avoid != 0 {
				fmt.Printf(" avoid=%v", s.RegMaskString(avoid))
			}
			fmt.Println()
		}
	}
}

// desiredState表示所需的寄存器分配。
type desiredState struct {
	// 期望的任务会很小，所以我们只使用一个列表
	// valueID+寄存器项的数目。
	entries []desiredStateEntry
	// 其他值要包含在其中的寄存器。该值将
	// 至少包含项的regs字段的并集，但
	// 可能包含以前输入的值的其他条目
	// 这种数据结构，但不再适用。
	avoid regMask
}
type desiredStateEntry struct {
	// （预regalloc）值
	ID ID
	// 它希望处于的寄存器，按优先级顺序。
	// 未使用的插槽将填充noRegister。
	regs [4]register
}

func (d *desiredState) clear() {
	d.entries = d.entries[:0]
	d.avoid = 0
}

// get返回值vid所需寄存器的列表。
func (d *desiredState) get(vid ID) [4]register {
	for _, e := range d.entries {
		if e.ID == vid {
			return e.regs
		}
	}
	return [4]register{noRegister, noRegister, noRegister, noRegister}
}

// 添加我们希望value vid位于寄存器r中的记录。
func (d *desiredState) add(vid ID, r register) {
	d.avoid |= regMask(1) << r
	for i := range d.entries {
		e := &d.entries[i]
		if e.ID != vid {
			continue
		}
		if e.regs[0] == r {
			// 已知和最高优先级
			return
		}
		for j := 1; j < len(e.regs); j++ {
			if e.regs[j] == r {
				// 从较低优先级移动到最高优先级
				copy(e.regs[1:], e.regs[:j])
				e.regs[0] = r
				return
			}
		}
		copy(e.regs[1:], e.regs[:])
		e.regs[0] = r
		return
	}
	d.entries = append(d.entries, desiredStateEntry{vid, [4]register{r, noRegister, noRegister, noRegister}})
}

func (d *desiredState) addList(vid ID, regs [4]register) {
	// regs是按优先级排序的，所以按相反的顺序迭代。
	for i := len(regs) - 1; i >= 0; i-- {
		r := regs[i]
		if r != noRegister {
			d.add(vid, r)
		}
	}
}

// clobber清除集合m中任何所需的寄存器。
func (d *desiredState) clobber(m regMask) {
	for i := 0; i < len(d.entries); {
		e := &d.entries[i]
		j := 0
		for _, r := range e.regs {
			if r != noRegister && m>>r&1 == 0 {
				e.regs[j] = r
				j++
			}
		}
		if j == 0 {
			// 此值不再需要寄存器。
			d.entries[i] = d.entries[len(d.entries)-1]
			d.entries = d.entries[:len(d.entries)-1]
			continue
		}
		for ; j < len(e.regs); j++ {
			e.regs[j] = noRegister
		}
		i++
	}
	d.avoid &^= m
}

// copy从另一个desiredState x复制所需状态。
func (d *desiredState) copy(x *desiredState) {
	d.entries = append(d.entries[:0], x.entries...)
	d.avoid = x.avoid
}

// remove移除所需的vid寄存器并返回它们。
func (d *desiredState) remove(vid ID) [4]register {
	for i := range d.entries {
		if d.entries[i].ID == vid {
			regs := d.entries[i].regs
			d.entries[i] = d.entries[len(d.entries)-1]
			d.entries = d.entries[:len(d.entries)-1]
			return regs
		}
	}
	return [4]register{noRegister, noRegister, noRegister, noRegister}
}

// 合并将另一个所需的状态x合并到d中。
func (d *desiredState) merge(x *desiredState) {
	d.avoid |= x.avoid
	// 应该只有几个需要的寄存器，所以
	// 线性插入是可以的。
	for _, e := range x.entries {
		d.addList(e.ID, e.regs)
	}
}

func min32(x, y int32) int32 {
	if x < y {
		return x
	}
	return y
}
func max32(x, y int32) int32 {
	if x > y {
		return x
	}
	return y
}
